package main;


import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import parsers.JSONParser;
import timertasks.CleanAllDataTimerTask;
import timertasks.RefreshLocalDataTimerTask;
import timertasks.TS3KeepAliveTask;
import utils.TS3;
import utils.Utils;
import web.interfaces.TankSicherheitModul;
import config.Constants;
import data.bot.AbstractBattleContainer;
import data.bot.AbstractBattleContainer.StringMode;
import data.bot.CombatContainer;
import data.bot.LandingContainer;
import data.wg.AbstractWoTCWBotListEntity;
import data.wg.WGCombat;
import data.wg.WGCombatant;
import data.wg.WGProvince;
import data.wg.WGScheduledBattle;
import de.stefan1200.jts3serverquery.JTS3ServerQuery;

public class WoTCWBot implements web.interfaces.TankSicherheitModul
{
	public static org.apache.log4j.Logger LOGGER = Logger.getLogger(WoTCWBot.class);
	public Calendar lastNotification;
	
	public static Timer timer = new Timer();
	
	public TS3 ts3;
	public JSONParser json;
	public static WoTCWBot bot;
	
	public static List<WGProvince> ownRiotingProvincesList = new ArrayList<WGProvince>();
	
	
	public static List<WGScheduledBattle> scheduledBattlesOfOwnClanList = new ArrayList<WGScheduledBattle>();
	
	public static List<AbstractBattleContainer> finalInvolvedContainerList = new ArrayList<AbstractBattleContainer>();
	public static List<AbstractBattleContainer> unfinalInvolvedContainerList = new ArrayList<AbstractBattleContainer>();
	public static boolean sendNewBattlePlan=false;
	
	public volatile String status = "off yet";
	public boolean running=false;
	
	public WoTCWBot()
	{
		
	}
	/*
	 * processors
	 */
	public void processRefreshLocalDataTimerTask()
	{
		status = "refreshing local data";
		LOGGER.info("fetching new data from WG");
		json.parseBattleScheduleOfOwnClan();
		json.parseRegions();
		LOGGER.info("parsing complete: final="+finalInvolvedContainerList.size()+" unfinal="+unfinalInvolvedContainerList.size()+" scheduled="+scheduledBattlesOfOwnClanList.size());
		sendChangedBattlePlan();
		status = "idle (waiting for schedulers)";
	}
	
	public void processBattleReminderTimerTask(AbstractBattleContainer c, int minutesRemaining)
	{
		LOGGER.info("BattleReminder ("+minutesRemaining+")"); //TODO
		String text = "\r\n"+(minutesRemaining-15)+" Minutes until "+c.getString(StringMode.BattleReminderTaskString);
		if(!ts3.send(text))
		{
			LOGGER.error("could not send BattleReminderText: "+ts3.getLastError());
		}
	}

	public void processNotifyClientEnterView(HashMap<String, String> eventInfo)
	{
		LOGGER.trace("clientEnterView");
//		if(!ts3.query.sendTextMessage(Integer.parseInt(bla), JTS3ServerQuery.TEXTMESSAGE_TARGET_CLIENT, getFormattedInvolvedCombatsList()))
//		{
//			System.out.println(ts3.query.getLastError());;
//		}
	}
	public void processCleanAllDataTimerTask()
	{
		LOGGER.info("cleaning all local data");
		ownRiotingProvincesList.clear();
		finalInvolvedContainerList.clear();
		unfinalInvolvedContainerList.clear();
		scheduledBattlesOfOwnClanList.clear();
		LOGGER.info("local data cleaned successfully");
	}
	public void processKeepAliveTimerTask()
	{
		ts3.keepalive();
	}

	
	
	/*
	 * handlers
	 */
	public void handleInvolvedWGCombat(WGCombat tempWGCombat,WGCombatant tempWGCombatant, WGProvince wgprovince)
	{
		CombatContainer tempCombatContainer = new CombatContainer(tempWGCombat);
		tempCombatContainer.calculateTimeAndFinalityOfCombat(tempWGCombatant, wgprovince.clan,wgprovince.primetime);
		LOGGER.debug("handling involved Combat("+tempWGCombat.id+") on "+wgprovince.name);
		
		CombatContainer prevFinal = (CombatContainer) findContainerById(tempCombatContainer.idOfContent, finalInvolvedContainerList);
		CombatContainer prevUnfinal = (CombatContainer) findContainerById(tempCombatContainer.idOfContent, unfinalInvolvedContainerList);
		
		
		if(prevFinal==null && prevUnfinal==null)
		{
			LOGGER.trace("combat was not yet saved");
			sendNewBattlePlan=true;
			
			if(tempCombatContainer.isFinal)
			{
				//FINAL
				LOGGER.trace("combat is \"final\", adding combat");
				finalInvolvedContainerList.add(tempCombatContainer);
				tempCombatContainer.createTimerTasks(30,20,15);
			}
			else
			{
				//UNFINAL
				LOGGER.trace("combat is \"unfinal\", adding combat");
				unfinalInvolvedContainerList.add(tempCombatContainer);
			}
		}
		else if(prevFinal!=null&&prevUnfinal==null)
		{
			LOGGER.trace("combat was saved as \"final\"");
			if(!tempCombatContainer.equals(prevFinal))
			{
				if(tempCombatContainer.isFinal)
				{
					LOGGER.info("combat changed (calculated time is different) - disabling old reminder-timers and adding new ones");
					sendNewBattlePlan=true;
					//WAS FINAL, BUT CHANGED!
					prevFinal.disableTimerTasks();
					finalInvolvedContainerList.remove(prevFinal);
					finalInvolvedContainerList.add(tempCombatContainer);
					tempCombatContainer.createTimerTasks(30,20,15);
				}
				else
				{
					LOGGER.error("combat is not final anymore!");
				}
		
			}
			else
			{
				LOGGER.trace("combat was not changed");
			}
		}
		else if(prevFinal==null&&prevUnfinal!=null)
		{
			LOGGER.trace("combat was saved as \"unfinal\"");
			if(!tempCombatContainer.equals(prevUnfinal))
			{
				LOGGER.trace("combat changed (calculated time is different)");
				sendNewBattlePlan=true;
				if(tempCombatContainer.isFinal)
				{
					//NOW FINAL
					LOGGER.trace("combat is now \"final\", adding reminder-timers");
					unfinalInvolvedContainerList.remove(prevUnfinal);
					finalInvolvedContainerList.add(tempCombatContainer);
					tempCombatContainer.createTimerTasks(30,20,15);
				}
				else
				{
					LOGGER.trace("combat changed (calculated time is different), but still not final");
					unfinalInvolvedContainerList.remove(prevUnfinal);
					unfinalInvolvedContainerList.add(tempCombatContainer);
				}
			}
			else
			{
				LOGGER.trace("combat was not changed");
			}
		}
		else
		{
			LOGGER.fatal("combat neither new, nor \"final\" or \"unfinal\"");
		}
		LOGGER.debug("handleInvolvedCombat finished");
	}
	
	public void handleLanding(WGProvince wgprovince)
	{
		LandingContainer lc = new LandingContainer(wgprovince);
		lc.calculateTimeAndFinalityOfCombat();
		LOGGER.debug("handling involved landing ("+wgprovince.name+") with id="+lc.idOfContent);
		
		AbstractBattleContainer prevFinal =  findContainerById(lc.idOfContent, finalInvolvedContainerList);
		AbstractBattleContainer prevUnfinal =  findContainerById(lc.idOfContent, unfinalInvolvedContainerList);
		
		LOGGER.debug("handling rioting province "+wgprovince.name);
		
		
		if(prevFinal==null && prevUnfinal==null)
		{
			LOGGER.trace("landing was not yet saved");
			sendNewBattlePlan=true;
			if(lc.isFinal)
			{
				LOGGER.trace("landing is \"final\", adding landing");
				finalInvolvedContainerList.add(lc);
				lc.createTimerTasks(30,20,15);
			}
			else
			{
				LOGGER.trace("landing is \"unfinal\", adding landing");
				unfinalInvolvedContainerList.add(lc);
			}
		}
		else if(prevFinal!=null&&prevUnfinal==null)
		{
			LOGGER.trace("landing was saved as \"final\"");
			if(!lc.equals(prevFinal))
			{
				if(lc.isFinal)
				{
					LOGGER.warn("landing changed (calculated time is different) - disabling old reminder-timers and adding new ones");
					sendNewBattlePlan=true;
					//WAS FINAL, BUT CHANGED!
					prevFinal.disableTimerTasks();
					finalInvolvedContainerList.remove(prevFinal);
					finalInvolvedContainerList.add(lc);
					lc.createTimerTasks(30,20,15);
				}
				else
				{
					LOGGER.error("landing is not final anymore!");
				}
		
			}
			else
			{
				LOGGER.trace("landing was not changed");
			}
		}
		else if(prevFinal==null&&prevUnfinal!=null)
		{
			LOGGER.trace("landing was saved as \"unfinal\"");
			if(!lc.equals(prevUnfinal))
			{
				LOGGER.trace("landing changed (calculated time is different)");
				sendNewBattlePlan=true;
				if(lc.isFinal)
				{
					//NOW FINAL
					LOGGER.trace("landing is now \"final\", adding reminder-timers");
					unfinalInvolvedContainerList.remove(prevUnfinal);
					finalInvolvedContainerList.add(lc);
					lc.createTimerTasks(30,20,15);
				}
				else
				{
					LOGGER.trace("lc changed (calculated time is different), but still not final");
					unfinalInvolvedContainerList.remove(prevUnfinal);
					unfinalInvolvedContainerList.add(lc);
				}
			}
			else
			{
				LOGGER.trace("landing was not changed");
			}
		}
		else
		{
			LOGGER.fatal("landing neither new, nor \"final\" or \"unfinal\"");
		}
		
	}
	

	/*
	 * helpfuctions
	 */
	public void sendChangedBattlePlan()
	{
		LOGGER.trace("sending battleplan, if neccessary");
		if(sendNewBattlePlan)
		{
			LOGGER.debug("preparing battleplan");
			String plan = getChangedBattlePlan();
			if(plan.length()>0)
			{
				sendNewBattlePlan=false;
				LOGGER.debug("sending battleplan");
				String text = "\r\n---------------- BATTLE SCHEDULE ----------------\r\n";
				text+=plan;
				text+="\r\n-------------------------------------------------------";
				
				LOGGER.info(text);
				if(!ts3.send(text))
				{
					LOGGER.fatal("could not send battleplan: "+ts3.getLastError());
				}
				else
				{
					LOGGER.info("battleplan sent successfully");
				}
			}
			else
			{
				LOGGER.debug("no valid battleplan found (plan.length==0)");
			}
		}
		else
		{
			LOGGER.trace("no battleplan neccessary");
		}
	}
	
	public String getChangedBattlePlan()
	{
		String text="";
		Collections.sort(finalInvolvedContainerList);
		Collections.sort(unfinalInvolvedContainerList);
		long now = Calendar.getInstance().getTimeInMillis();
		LOGGER.trace("checking "+finalInvolvedContainerList.size()+" \"final\" containers");
		boolean send=false;
		
		
		for(AbstractBattleContainer cc:finalInvolvedContainerList)
		{
			long dday  = now/1000/60/60/24;
			long tomorrow = dday*1000*60*60*24 + 60*60*1000*24;
			if(cc.time*1000>now && cc.time*1000<tomorrow)
			{
				send=true;
				text+=cc.getString(StringMode.CombatList)+"\r\n";
			}
			else
			{
				LOGGER.trace("not mentioning \"final\" BattleContainer with ID "+cc.idOfContent+" because of it's time: "+ Utils.getDateString(cc.time));
			}
			
		}
		LOGGER.trace("checking "+unfinalInvolvedContainerList.size()+" \"unfinal\" containers");
		for(AbstractBattleContainer cc:unfinalInvolvedContainerList)
		{
			long dday  = now/1000/60/60/24;
			long tomorrow = dday*1000*60*60*24 + 1000*60*60*24;
			if(cc.time*1000>now   && cc.time*1000<tomorrow )  // cc.time*1000 - 1000*60*60*5 < now 
			{
				text+=cc.getString(StringMode.CombatList)+"\r\n";
				if(cc.time*1000 - 1000*60*60*5 < now)
				{
					send=true;
				}
			}
			else
			{
				LOGGER.trace("not mentioning \"unfinal\" BattleContainer with ID "+cc.idOfContent+" because of it's time: " +Utils.getDateString(cc.time));
			}
		}
		if(send)
		{
			return text;
		}
		return "";
	}
	
	

	
	
	
	/*
	 * listfunctions
	 */
	
	public <T extends AbstractWoTCWBotListEntity> T findAWBLEById(int id, List<T> list)
	{
		for(T t:list)
		{
			if(t.id==id)
			{
				return t;
			}
		}
		return null;
	}
	
	public AbstractBattleContainer findContainerById(int id, List<AbstractBattleContainer> list)
	{
		for(AbstractBattleContainer abc: list)
		{
			if(abc.idOfContent==id)
			{
				return abc;
			}
		}
		return null;
	}
	
	public static WGScheduledBattle findScheduledBattleByName(String nameOfProvince)
	{
		for(WGScheduledBattle sd: scheduledBattlesOfOwnClanList)
		{
			if(sd.nameOfProvince.equals(nameOfProvince))
			{
				return sd;
			}
		}
		return null;
	}
	
	

	

	public static void main(String[] args) {
		//PropertyConfigurator.configure("Logger.properties");
		WoTCWBot.bot = new WoTCWBot();
	}

	@Override
	public void run() {
		running=true;
		System.out.println("wotcwbot started");
		status="starting (loading ini)";
		LOGGER.info("starting WoTCWBot");
		LOGGER.info("loading ini File");
		System.out.println("loading ini");
		if(Constants.loadIni())
		{
			ts3 = new TS3(this);
			json = new JSONParser(this);
			status = "starting (setting up parsers)";
			if(json.init())
			{
				status = "starting (setting up ts3 connection)";
				if(ts3.init())
				{
					Calendar c = Calendar.getInstance();
					c.add(Calendar.DAY_OF_MONTH, 1);
					c.set(Calendar.HOUR_OF_DAY, 5);
					c.set(Calendar.MINUTE, 0);
					timer.scheduleAtFixedRate(new CleanAllDataTimerTask(this), c.getTime(), 1000*60*60*24);
					timer.scheduleAtFixedRate(new TS3KeepAliveTask(this), Calendar.getInstance().getTime(), 1000*60*2);
					
					LOGGER.info("WoTCWBot initialized successfully");
					timer.scheduleAtFixedRate(new RefreshLocalDataTimerTask(this), 0, 1000*60*5);
					status = "idle (waiting for schedulers)";
				}
				else
				{
					status = "off (ts3 init failed)";
				}
			}
			else
			{
				status = "off (parser init failed)";
			}
		}
		else
		{
			status = "off (loading config failed)";
		}
		
	}

	@Override
	public String getStatus() {
		return status;
	}

	@Override
	public String getDisplayName() {
		return "WoTCWBot";
	}

	@Override
	public void startup() {
		new Thread(this).start();
	}
	@Override
	public void shutdown()
	{
		timer.cancel();
		status = "off";
		running=false;
	}
	@Override
	public boolean isRunning() {
		return running;
	}

	

	
}
